#include "stdafx.h"
#include "Thread.h"
#include <utility/TimeVal.h>
#ifndef WIN32
#include <sys/select.h>
#endif

namespace serika
{
	namespace threading
	{
		using namespace ut;

		Thread::Thread()
		{
	#ifdef WIN32
			mHandle = 0;
			mId = 0;
	#else
			mThread = 0;
	#endif
			mStarted = 0;
		}
	
		Thread::~Thread()
		{
	#ifdef WIN32
			if (mHandle != 0)
			{
				CloseHandle(mHandle);
				mHandle = 0;
			}
	#endif
		}
	
	#ifdef WIN32
		DWORD WINAPI ThreadFunc(void* thread)
	#else
		void* ThreadFunc(void* thread)
	#endif
		{
			Thread* t = (Thread*)thread;
			t->mStarted = true;
			t->Run();
			t->mStarted = false;
			return 0;
		}
	
		bool Thread::Start()
		{
			if (mStarted)
			{
				return false;
			}
	
	#ifdef WIN32
			mHandle = CreateThread(0, 0, ThreadFunc, this, 0, &mId);
			return mHandle != 0;
	#else
			return pthread_create(&mThread, 0, ThreadFunc, this) == 0;
	#endif
		}
	
		void Thread::Join()
		{
			if (!mStarted)
			{
				return;
			}
	#ifdef WIN32
			WaitForSingleObject(mHandle, INFINITE);
	#else
			void* status;
			pthread_join(mThread, &status);
	#endif
		}
	
		Thread::ID Thread::GetThreadId() const
		{
	#ifdef WIN32
			return mId;
	#else
			return mThread;
	#endif
		}
	
		Thread::ID Thread::GetCurrentThreadId()
		{
	#ifdef WIN32
			return ::GetCurrentThreadId();
	#else
			return pthread_self();
	#endif
		}
	
		bool Thread::operator ==(const Thread& other) const
		{
	#ifdef WIN32
			return mHandle == other.mHandle && mId == other.mId && mHandle != 0 && other.mHandle != 0;
	#else
			return pthread_equal(mThread, other.mThread) != 0;
	#endif
		}
	
		bool Thread::operator !=(const Thread& other) const
		{
			return !(*this == other);
		}
	
		void Thread::Sleep(const TimeVal& interval)
		{
	#ifdef WIN32
			::Sleep(interval.tv_sec * 1000 + interval.tv_usec / 1000);
	#else
			TimeVal tv = interval;
			select(0, NULL, NULL, NULL, &tv);
	#endif
		}
	
		void Thread::Sleep(unsigned long milliseconds)
		{
	#ifdef WIN32
			::Sleep(milliseconds);
	#else
			TimeVal tv;
		    tv.tv_sec = milliseconds / 1000;
			tv.tv_usec = milliseconds % 1000 * 1000;
			select(0, NULL, NULL, NULL, &tv);
	#endif
		}
	
		void Thread::SchedYield()
		{
	#ifdef WIN32
			::Sleep(0);
	#else
			sched_yield();
	#endif
		}
	}
}
